package com.etl.runner;

import com.eksframework.commons.core.base.EksList;
import com.etl.sys.entity.SysLock;
import com.etl.sys.entity.SysSchedule;
import com.etl.sys.service.ISysLockService;
import com.etl.sys.service.ISysScheduleService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.annotation.Order;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.stereotype.Component;

import java.time.Duration;
import java.time.Instant;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * 调度初始化运行类，生成加载调度任务定时器
 *
 * @author: chenfenghai
 * @version: 1.0
 * @date: 2020-10-14 15:00
 */
@Slf4j
@Order(0)
@Component
public class ApplicationLockRunner implements ApplicationRunner {

    @Autowired
    ISysLockService sysLockService;

    @Autowired
    ISysScheduleService sysScheduleService;

    @Autowired
    private ThreadPoolTaskScheduler threadPoolTaskScheduler;

    @Override
    public void run(ApplicationArguments args) throws InterruptedException {
        log.info("The Application LOCK_UID: {}", ISysLockService.LOCK_UID);
        threadPoolTaskScheduler.scheduleWithFixedDelay(
                sysLockService::saveLock, Duration.ofMinutes(1));
        TimeUnit.SECONDS.sleep(10);
        threadPoolTaskScheduler.scheduleWithFixedDelay(
                this::allocateLock,
                Instant.now().plusSeconds(ISysLockService.LOCK_SECOND),
                Duration.ofMinutes(2));
    }

    /**
     * 分配锁
     */
    private void allocateLock() {
        log.debug("Allocate lock to the schedule starting...");
        if (!sysLockService.isFirstLock()) {
            log.info("This application is not first lock.");
            return;
        }
        // 查询锁UID列表
        List<SysLock> lockList = sysLockService.lambdaQuery().select(SysLock::getLockUid)
                .eq(SysLock::getLockType, ISysLockService.LOCK_TYPE_SCHEDULE)
                .orderByAsc(SysLock::getLockId).list();
        if (CollectionUtils.isEmpty(lockList)) {
            log.info("This lock is empty. return.");
            return;
        }
        // 查询调度列表
        List<SysSchedule> scheduleAllList = sysScheduleService.lambdaQuery()
                .select(SysSchedule::getScheduleId, SysSchedule::getLockUid)
                .orderByAsc(SysSchedule::getCinemaId).list();
        List<String> scheduleLockUidList = scheduleAllList.parallelStream()
                .map(SysSchedule::getLockUid).distinct().sorted().collect(Collectors.toList());
        // 把锁UID组装成列表
        List<String> lockUidList = lockList.parallelStream()
                .map(SysLock::getLockUid).sorted().collect(Collectors.toList());
        // 分组没有变化
        if (Arrays.equals(lockUidList.toArray(), scheduleLockUidList.toArray())) {
            log.info("This lock no change. return.");
            return;
        }
        // 分配到各组
        List<List<SysSchedule>> groupScheduleList = EksList.group(scheduleAllList, lockList.size());
        for (int i = 0; i < groupScheduleList.size(); i++) {
            sysScheduleService.lambdaUpdate()
                    .set(SysSchedule::getLockUid, lockList.get(i).getLockUid())
                    .in(SysSchedule::getScheduleId,
                            groupScheduleList.get(i).stream().map(SysSchedule::getScheduleId)
                                    .collect(Collectors.toList())).update();
        }
        log.debug("Allocate lock to the schedule completed.");
    }
}
